package org.linitly.boot.base.utils.auth.strategy;

import io.jsonwebtoken.ExpiredJwtException;
import org.apache.commons.lang3.StringUtils;
import org.linitly.boot.base.enums.ResultEnum;
import org.linitly.boot.base.exception.CommonException;
import org.linitly.boot.base.utils.bean.SpringBeanUtil;
import org.linitly.boot.base.utils.jwt.AbstractJwtUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author: linitly
 * @date: 2021/5/24 17:02
 * @descrption: 双token模式，可自动续期
 */
public class DoubleTokenModule extends AbstractTokenModule {

    private static volatile DoubleTokenModule doubleTokenModule;

    DoubleTokenModule(AbstractJwtUtil jwtUtil) {
        super(jwtUtil);
    }

    public static DoubleTokenModule getInstance(AbstractJwtUtil jwtUtil) {
        if (doubleTokenModule == null) {
            synchronized (DoubleTokenModule.class) {
                if (doubleTokenModule == null) {
                    doubleTokenModule = new DoubleTokenModule(jwtUtil);
                }
            }
        }
        return doubleTokenModule;
    }

    @Override
    public void setTokensHead(String tokenResponseHeadKey, String refreshTokenResponseHeadKey, String token, String refreshToken) {
        HttpServletResponse response = SpringBeanUtil.getResponse();
        response.setHeader(tokenResponseHeadKey, token);
        response.setHeader(refreshTokenResponseHeadKey, refreshToken);
    }

    @Override
    public void interceptorValid(String tokenResponseHeadKey) {
        HttpServletRequest request = SpringBeanUtil.getRequest();
        String token = jwtUtil.getToken(request);
        String refreshToken = jwtUtil.getRefreshToken(request);
        if (StringUtils.isBlank(token) || StringUtils.isBlank(refreshToken)) {
            throw new CommonException(ResultEnum.UNAUTHORIZED);
        }
        try {
            jwtUtil.parseToken(token);
        } catch (ExpiredJwtException e) {
            tokenExpired(token, refreshToken, tokenResponseHeadKey);
            return;
        }
        validToken(token);
        jwtUtil.parseRefreshToken(refreshToken);
        validRefreshToken(refreshToken);
    }

    private void tokenExpired(String token, String refreshToken, String tokenResponseHeadKey) {
        jwtUtil.parseRefreshToken(refreshToken);
        validRefreshToken(refreshToken);
        validExpiredToken(token);
        generateNewToken(tokenResponseHeadKey);
    }

    @Override
    public void validToken(String token) {
        String userId = getUserId();
        String redisToken = auth.getToken(userId);
        if (StringUtils.isBlank(redisToken)) {
            throw new CommonException(ResultEnum.LOGIN_FAILURE);
        } else if (!token.equals(redisToken)) {
            throw new CommonException(ResultEnum.REMOTE_LOGIN);
        }
    }

    @Override
    public void validExpiredToken(String token) {
        String userId = getUserId();
        String lastExpiredToken = auth.getLastExpiredToken(userId);
        if (StringUtils.isBlank(lastExpiredToken) || !token.equals(lastExpiredToken)) {
            auth.setLastExpiredToken(userId, token);
            return;
        }
        // 缓存中过期token不为空且与token一致
        throw new CommonException(ResultEnum.LOGIN_FAILURE);
    }

    @Override
    public void validRefreshToken(String refreshToken) {
        String userId = getUserId();
        String redisRefreshToken = auth.getRefreshToken(userId);
        if (StringUtils.isBlank(redisRefreshToken)) {
            throw new CommonException(ResultEnum.LOGIN_FAILURE);
        } else if (!refreshToken.equals(redisRefreshToken)) {
            throw new CommonException(ResultEnum.REMOTE_LOGIN);
        }
    }

    @Override
    public void generateNewToken(String tokenResponseHeadKey) {
        String userId = getUserId();
        String newToken = jwtUtil.generateToken(userId);
        auth.newTokenRedisSet(userId, newToken);
        SpringBeanUtil.getResponse().setHeader(tokenResponseHeadKey, newToken);
    }
}
